package com.okcl.manager.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.okcl.common.exception.BusinessException;
import com.okcl.manager.constant.RedisKeyConstants;
import com.okcl.manager.mapper.SysMenuMapper;
import com.okcl.manager.mapper.SysUserMapper;
import com.okcl.manager.mapper.SysUserRoleMapper;
import com.okcl.manager.service.SysUserService;
import com.okcl.manager.utils.MenuHelper;
import com.okcl.model.dto.system.AssginRoleDto;
import com.okcl.model.dto.system.LoginDto;
import com.okcl.model.dto.system.SysUserDto;
import com.okcl.model.entity.system.SysMenu;
import com.okcl.model.entity.system.SysUser;
import com.okcl.model.vo.common.ResultCodeEnum;
import com.okcl.model.vo.h5.UserInfoVo;
import com.okcl.model.vo.system.LoginVo;
import com.okcl.model.vo.system.SysMenuVo;
import com.okcl.utils.AuthContextUtil;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class SysUserServiceImpl implements SysUserService {
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private SysUserRoleMapper sysUserRoleMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private SysMenuMapper sysMenuMapper;

    /**
     * 登录
     *
     * @param loginDto
     * @return
     */
    @Override
    public LoginVo login(LoginDto loginDto) {
        //获取提交用户名
        String userName = loginDto.getUserName();
        SysUser sysUser = sysUserMapper.selectUserInfoByUserName(userName);
        if (sysUser == null) {
            throw new BusinessException(ResultCodeEnum.USER_NAME_IS_NOT_EXISTS);
        }
        String db_password = sysUser.getPassword();
        String ip_password = loginDto.getPassword();
        String jm_password = DigestUtils.md5DigestAsHex(ip_password.getBytes());
        if (!db_password.equals(jm_password)) {
            throw new BusinessException(ResultCodeEnum.LOGIN_ERROR);
        }
        String codeKey = loginDto.getCodeKey();//前端传递过来的验证码key
        String captcha = loginDto.getCaptcha();//前端传递过来的验证码
        String redisCaptcha = stringRedisTemplate.opsForValue().get(RedisKeyConstants.VALIDATE_CODE_KEY + codeKey);
        //校验验证码是否已经过期
        if (!StringUtils.hasText(redisCaptcha)) {
            throw new BusinessException(ResultCodeEnum.VALIDATE_ERROR);
        }
        //校验验证码是否正确
        if (!redisCaptcha.equals(captcha)) {
            throw new BusinessException(ResultCodeEnum.VALIDATE_ERROR);
        }
        //登录成功删除验证码
        stringRedisTemplate.delete(RedisKeyConstants.VALIDATE_CODE_KEY + codeKey);
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        LoginVo loginVo = new LoginVo();
        loginVo.setToken(token);
        String sysUserStr = JSON.toJSONString(sysUser);
        stringRedisTemplate.opsForValue().set(RedisKeyConstants.LOGIN_KEY + token, sysUserStr, 7, TimeUnit.DAYS);
        return loginVo;
    }

    /**
     * 获取当前登录用户信息
     *
     * @return
     */
    @Override
    public UserInfoVo getUserInfo(String token) {
        if (!StringUtils.hasText(token)) {
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);
        }
        //从redis里面查询数据
        String value = stringRedisTemplate.opsForValue().get(RedisKeyConstants.LOGIN_KEY + token);
        if (!StringUtils.hasText(value)) {
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);
        }
        //反序列化redis中的用户信息
        JSONObject sysUserStr = JSONUtil.parseObj(value);
        SysUser sysUser = JSONUtil.toBean(sysUserStr, SysUser.class);
        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setNickName(sysUser.getName());
        userInfoVo.setAvatar(sysUser.getAvatar());
        return userInfoVo;
    }

    /**
     * 用户退出
     *
     * @param token
     */
    @Override
    public void logout(String token) {
        if (!StringUtils.hasText(token)) {
            throw new BusinessException(ResultCodeEnum.LOGIN_AUTH);
        }
        //清除redis中的用户信息
        stringRedisTemplate.delete(RedisKeyConstants.LOGIN_KEY + token);
        //清除local线程中的用户信息
        AuthContextUtil.remove();
    }

    /**
     * 查询用户列表
     *
     * @param sysUserDto
     * @return
     */
    @Override
    public PageInfo<SysUser> queryPageUserList(SysUserDto sysUserDto) {
        PageHelper.startPage(sysUserDto.getPageNum(), sysUserDto.getPageSize());
        List<SysUser> list = sysUserMapper.selectPageUserList(sysUserDto);
        return new PageInfo<>(list);
    }

    /**
     * 添加用户
     *
     * @param sysUser
     */
    @Override
    @Transactional
    public void save(SysUser sysUser) {
        int i = sysUserMapper.selectUserNameIsExists(sysUser.getUserName());
        if (i > 0) {
            throw new BusinessException(ResultCodeEnum.USER_NAME_IS_EXISTS);
        }
        //加密密码
        sysUser.setPassword(DigestUtils.md5DigestAsHex(sysUser.getPassword().getBytes()));
        sysUser.setUpdateTime(LocalDateTime.now());
        int t = sysUserMapper.save(sysUser);
        //设置创建时间
        if (t <= 0) {
            throw new BusinessException(ResultCodeEnum.FAILURE);
        }
    }

    /**
     * 修改用户
     *
     * @param sysUser
     */
    @Override
    public void edit(SysUser sysUser) {
        sysUser.setUpdateTime(LocalDateTime.now());
        int edit = sysUserMapper.edit(sysUser);
        if (edit <= 0) {
            throw new BusinessException(500, "修改失败");
        }
    }

    /**
     * 删除用户
     *
     * @param id
     */
    @Override
    public void delete(Long id) {
        sysUserMapper.deleteByLogical(id);
    }

    /**
     * 为用户分配角色
     *
     * @param assginRoleDto
     */
    @Transactional
    @Override
    public void assignRole(AssginRoleDto assginRoleDto) {
        Long userId = assginRoleDto.getUserId();
        List<Long> roleIdList = assginRoleDto.getRoleIdList();
        if (userId == null) {
            throw new BusinessException(ResultCodeEnum.DATA_ERROR);
        }
        if (roleIdList == null || roleIdList.isEmpty()) {
            //先删除之前的用户角色关系数据
            sysUserRoleMapper.deleteByUserId(assginRoleDto.getUserId());
            return;
        }
        //先删除之前的用户角色关系数据
        sysUserRoleMapper.deleteByUserId(assginRoleDto.getUserId());
        //再添加新的用户角色关系数据
        sysUserRoleMapper.save(assginRoleDto.getUserId(), assginRoleDto.getRoleIdList());
    }

    /**
     * 获取用户角色id列表
     *
     * @param id
     * @return
     */
    @Override
    public List<Long> getRoleIdList(Long id) {
        //查询用户角色关系数据
        return sysUserRoleMapper.selectRoleIdList(id);
    }

    /**
     * 获取用户菜单列表
     *
     * @return
     */
    @Override
    public List<SysMenuVo> queryMenuByUserId() {
        //获取当前线程用户
        Long id = AuthContextUtil.getUser().getId();
        List<SysMenu> sysMenus = sysMenuMapper.queryMenuByUserId(id);
        //先封装成树结构
        List<SysMenu> sysMenuList = MenuHelper.buildTree(sysMenus);
        //将sysMenu对象转换成sysMenuVo对象
        return this.convert(sysMenuList);
    }

    //将sysMenu对象转换成sysMenuVo对象
    private List<SysMenuVo> convert(List<SysMenu> sysMenus) {
        //对传入的集合进行判断空处理
        if (CollectionUtils.isEmpty(sysMenus)) {
            return new ArrayList<>();
        }
        //创建集合存储
        List<SysMenuVo> sysMenuVoList = new LinkedList<SysMenuVo>();
        sysMenus.forEach(sysMenu -> {
            SysMenuVo sysMenuVo = new SysMenuVo();
            sysMenuVo.setTitle(sysMenu.getTitle());
            sysMenuVo.setName(sysMenu.getComponent());
            List<SysMenu> children = sysMenu.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                //执行递归处理相同子菜单的逻辑
                sysMenuVo.setChildren(convert(children));
            }
            sysMenuVoList.add(sysMenuVo);
        });
        return sysMenuVoList;
    }
}
